import { Utils, Logger } from 'orion-common'
import Konva from 'konva'
import TNode from './node-base.js'
import TUtils from './node-utils.js'

 /**
  * 实体节点类，带文字标签，带扩展按钮，可以有子节点
  */
class TEntityNode extends TNode {
  constructor (config) {
    super(Utils.lodash.merge({
      acceptChild: true,
      fill: '#483D8B',
      stroke: '#191970',
      protos: { entity: true },
      title: 'Entity',  // 文字信息
      flagColor: '#A52A2A',
      canMove: true
    }, config))

    this.root.on('click', function (evt) {
      let node = this.getAttr('@node')
      let stage = node.stage()
      if (!stage) return

      stage.selectNode(node)
      evt.cancelBubble = true
    })
  }

  /**
   * get/set 标签颜色
   * @param {*} color 
   * @returns 
   */
  flagColor(color) {
    if (color) {
      this.config.flagColor = color
      this.colorBg.fill(color)
    } else {
      return this.colorBg.fill()
    }
  }

  /**
   * set/get 标签文字
   * @param {*} nm 
   * @returns 
   */
  title(text) {
    if (text) {
      this.config.title = ntext
      this.title.text(text)
    } else {
      return this.title.text()
    }
  }

  /**
   * 创建背景
   */
  createBackground () {
    let group = new Konva.Group({
      x: 0,
      y: 0,
    })

    this.colorBg = new Konva.Rect({
      x: -TUtils.node.background.offset,
      y: -TUtils.node.background.offset,
      width: TUtils.node.background.width,
      height: TUtils.node.background.height,
      cornerRadius: 4,
      fill: this.config.flagColor,
      stroke: this.config.flagColor,
      strokeWidth: this.config.strokeWidth,
      name: 'color-flag'
    })
    group.add(this.colorBg)

    let rectBg =  new Konva.Rect({
      x: 0,
      y: 0,
      width: this.config.width,
      height: this.config.height,
      fill: this.config.fill,
      stroke: this.config.stroke,
      strokeWidth: this.config.strokeWidth,
      cornerRadius: 4,
      listening: true
    })
    group.add(rectBg)
    this.root.add(group)

    return rectBg
  }

  /**
   * 创建连线区域
   */
  createLinkZone() {
    let width = this.config.width
    let height = this.config.height

    // 连线组
    this.links = []
    this.linkZone = new Konva.Group({
      x: width,
      y: height / 2,
      name: 'entity-linkzone'
    })
    this.root.add(this.linkZone)

    // 临时连线
    this.childZoneLink = new Konva.Arrow({
      x: 0,
      y: 0,
      points: [0, 0, 25, 0, TUtils.node.childSpace.horizonal, 0],
      pointerLength: TUtils.link.pointerSize,
      pointerWidth: TUtils.link.pointerSize,
      fill: TUtils.zone.highlight,
      stroke: TUtils.zone.highlight,
      strokeWidth: TUtils.link.strokeWidth,
      tension: 0.5,
      visible: false
    })
    this.linkZone.add(this.childZoneLink)
  }

  /**
   * 创建子节点区域
   */
  createChildZone() {
    let width = this.config.width
    let height = this.config.height

    this.childZone = new Konva.Group({
      x: width + TUtils.node.childSpace.horizonal,
      y: height / 2,
      name: 'entity-childzone'
    })
    this.root.add(this.childZone)

    // 临时占位子节点
    this.childZoneMarker = new Konva.Rect({
      x: 0,
      y: 0,
      width: TUtils.node.minWidth,
      height: 8,
      fill: TUtils.zone.highlight,
      visible: false
    })
    this.childZone.add(this.childZoneMarker)
  }
  /**
   * 创建节点内容
   */
  createBody () {
    super.createBody()

    this.body.x(TUtils.node.margin)
    this.body.y(TUtils.node.margin)
    this.body.width(this.config.width - 2 * TUtils.node.margin)
    this.body.height(this.config.height - 2 * TUtils.node.margin)

    if (this.canAcceptChild()) {

      this.createLinkZone()

      this.isExpanding = true
      this.expandBtn = this.createExpandButton({
        size: 12,
        action: this.toggleExpand.bind(this)
      })
      this.root.add(this.expandBtn)

      this.createChildZone()
    }

    // 标签文字
    this.title = new Konva.Text({
      x: 0,
      y: 0,
      width: this.body.width(),
      height: this.body.height(),
      align: 'center',
      verticalAlign: 'middle',
      text: this.config.title,
      fontSize: TUtils.label.fontSize,
      fill: TUtils.label.fontColor,
      listening: false
    })
    this.body.add(this.title)
  }

  /**
   * 创建展开按钮
   * @param option
   * {
   *   uid:
   *   x:
   *   y:
   *   size:
   *   background:
   *   action:
   * }
   */
  createExpandButton (option) {
    let opt = Utils.lodash.merge({
      x: 0,
      y: 0,
      size: 12,
      background: '#ddd',
      stroke: '#333',
      strokeWidth: 2,
      lineCap: 'round',
      lineJoin: 'round',
      isExpanding: true
    }, option)

    // expand按钮
    let expand = new Konva.Group({
      x: opt.x,
      y: opt.y,
      name: 'entity-expand'
    })

    let bg = new Konva.Circle({
      x: 0,
      y: 0,
      radius: opt.size / 2,
      fill: opt.background,
      stroke: opt.stroke,
      strokeWidth: 1
    })
    expand.add(bg)

    let lineRadius = opt.size / 2 - 3
    let hline = new Konva.Line({
      points: [-lineRadius, 0, lineRadius, 0],
      stroke: opt.stroke,
      strokeWidth: opt.strokeWidth,
      lineCap: 'round',
      lineJoin: 'round'
    })
    expand.add(hline)

    let vline = new Konva.Line({
      points: [0, -lineRadius, 0, lineRadius],
      stroke: opt.stroke,
      strokeWidth: opt.strokeWidth,
      lineCap: 'round',
      lineJoin: 'round',
      visible: !opt.isExpanding
    })
    expand.add(vline)
    expand.on('mousedown', opt.action)

    expand.on('mouseout', function () {
      vline.setAttr('stroke', opt.stroke)
      hline.setAttr('stroke', opt.stroke)
      this.getLayer().draw()
    })

    expand.on('mouseover', function () {
      vline.setAttr('stroke', '#FF3030')
      hline.setAttr('stroke', '#FF3030')
      this.getLayer().draw()
    })

    expand.setExpand = function (flag) {
      vline.visible(!flag)
      this.getLayer().draw()
    }

    return expand
  }

    /**
   * 计算索引
   * @param {*} oldIndex 
   * @param {*} newIndex 
   * @param length
   */
    computeValidIndex (oldIndex, newIndex, length) {
      if (length === 0) {
        return -1
      }
  
      if (length === 1) {
        return 0
      }
  
      if (newIndex < 0 || newIndex >= length) {
        return length - 1
      }
  
      if (newIndex === (oldIndex + 1)) {
        newIndex--
      }
      return newIndex
    }
  
  /**
   * 
   * @param {*} child 
   * @param {*} index 
   */
  insertChild (child, index) {
    
    if (!child) {
      Logger.error(`TEntityNode::insertChild failed -  child is null`)
      return
    }
    let childParent = child.parent()
    // 如果是同一个父节点，则仅调整索引
    if (childParent == this) {
      let oldIndex = this.children.indexOf(child)
      let newIndex = this.computeValidIndex(oldIndex, index, this.children.length)

      if (newIndex >= 0 && oldIndex !== newIndex) {
        [this.children[oldIndex], this.children[newIndex]] = [this.children[newIndex], this.children[oldIndex]]
      }
      this.adjust({
        upward: true
      })
    } else { // 从原节点删除，移到新节点中
      childParent.removeChild(child)
      //this.expandChildren(true)
      this.childZone.add(child.knode())
      this.children.splice(index, 0, child)
      this.addChildLink()
      this.adjust({
        downward: true,
        upward: true
      })
    }
  }

  /**
   * 
   * @param {*} child 
   */
  removeChild (child) {
    if (!child) {
      return
    }
    let index = this.children.indexOf(child)
    this.children.splice(index, 1)
    child.knode().remove()

    let link = this.links[index]
    link.destroy()
    this.links.splice(index, 1)

    this.adjust({
      downward: true,
      upward: true
    })
  }

  /**
   * 
   */
  removeChildren () {
    for (let i = 0; i < this.children.length; i++) {
      let child = this.children[i]
      child.knode().remove()
      this.links[i].destroy()
    }
    this.links = []
    let list = [].concat(this.children)
    this.children = []
    return list
  }

  /**
   * 
   */
  addChildLink () {
    let link = new Konva.Arrow({
      x: 0,
      y: 0,
      points: [0, 0, 25, 0, TUtils.node.childSpace.horizonal, 0],
      pointerLength: TUtils.link.pointerSize,
      pointerWidth: TUtils.link.pointerSize,
      fill: TUtils.link.fill,
      stroke:  TUtils.link.fill,
      strokeWidth: TUtils.link.strokeWidth,
      tension: 0.5,
      visible: this.isExpanding,
      listening: false
    })
    this.linkZone.add(link)
    this.links.push(link)
    return link
  }

  /**
   * 
   * @param {*} node 
   */
  addChild (child, adjust = true) {
    if (!child || !this.canAcceptChild()) {
      return
    }
    child.visible(this.isExpanding)
    this.childZone.add(child.knode())
    this.children.push(child)
    this.addChildLink()
    if (adjust) {
      this.adjust({
        downward: true,
        upward: true
      })
      this.refresh()
    }
  }

  /**
   * 判断插入位置
   * @param point // 画布点坐标
   * @return int 返回插入索引 -1 表示不能插入
   */
  intersection (point) {  
    let width = this.background.width()
    let pos = this.root.getAbsolutePosition()
    let zoom = this.stage().zoom
    // 判断投放区域，是实体右半边边缘，到子节点起始位置
    let zone = {
      left: pos.x + width * zoom * 0.5,
      right: pos.x + (width + TUtils.node.childSpace.horizonal) * zoom
    }
    // 首先判断X坐标处于位置区域
    if (point.x < zone.left || point.x > zone.right) {
      return -1
    }

    // 判断高度
    let index = -1
    let height = this.size().height
    // 如果没有子节点
    if (this.children.length === 0 || !this.isExpanding ) {
      if (point.y > pos.y && point.y < (pos.y + height * zoom)) {
        index = 0
      }
    } else {
      let childHeights = []
      let allowance = 0
      let zoneHeight = 0
      for (let child of this.children) {
        if(child.dragging()) continue

        let h = child.clientHeight()
        childHeights.push(h / 2 + allowance)
        allowance = h / 2 + TUtils.node.childSpace.vertical
        zoneHeight += (h + TUtils.node.childSpace.vertical)
      }
      childHeights.push(allowance)
      zoneHeight -= TUtils.node.childSpace.vertical
      zoneHeight = Math.max(height, zoneHeight)
 
      let offsetY = pos.y + (height - zoneHeight) / 2 * zoom
      for (let i = 0; i < childHeights.length; i++) {
        if ((i === 0) && point.y < offsetY) {
          break
        }
        offsetY += (childHeights[i] * zoom)
        if (point.y < offsetY) {
          index = i
          break
        }
      }
    }
    return index
  }

  /**
   * 
   */
  indexOfChild (child) {
    if (child instanceof TEntityNode) {
      return this.children.indexOf(child)
    }
    return -1
  }

  /**
   * 调整宽
   * @param {*} w 
   */
  resizeWidth (w) {
    // 调整背景
    super.resizeWidth(w)

    if (this.expandBtn) {
      let width = this.background.width()
      let height = this.background.height()
      // 调整按钮
      this.expandBtn.position({
        x: width,
        y: height / 2
      })
    }
    
    if (w) {
      let zoneWidth = w - 2 * (TUtils.zone.padding + TUtils.node.margin)
      this.childZone && this.childZone.x(w + TUtils.node.childSpace.horizonal)
      this.linkZone && this.linkZone.x(w)
    }
  }

  /**
   * 切换子节点展开/收起
   */
  toggleExpand () {
    this.isExpanding = !this.isExpanding
    this.expandChildren(this.isExpanding)
  }

  /**
   * 展开/收起子节点
   * @param {*} flag boolean
   */
  expandChildren (flag = true) {
    this.isExpanding = flag
    this.expandBtn.setExpand(flag)

    for (let child of this.children) {
      child.visible(flag)
    }
    for (let link of this.links) {
      link.visible(flag)
    }

    this.adjust({
      upward: true
    })
    this.refresh()
  }

  /**
   * 获取包含子节点的高度
   */
  childHeight () {
    let childHeight = 0
    if (this.isExpanding) {
      for (let child of this.children) {
        if (child.dragging()) continue
        childHeight += child.clientHeight()
        childHeight += TUtils.node.childSpace.vertical
      }
      childHeight -= TUtils.node.childSpace.vertical
    }
    return childHeight
  }

  /**
   * 获取包含子节点的高度
   */
  clientHeight () {
    return Math.max(this.childHeight(), this.background.height())
  }

  /**
   * 调整布局
   */
  adjust (option) {
    let opt = Utils.lodash.merge({
      downward: false,
      upward: false
    }, option)

    // 调整子节点布局
    if (opt.downward && this.isExpanding) {
      for (let child of this.children) {
        if (child.dragging()) continue

        child.adjust({
          downward: true
        })
      }
    }

    // 计算宽高
    let needWidth = 0
    let offsetY = TUtils.zone.height + 2 + TUtils.node.space

    needWidth = Math.max(TUtils.node.minWidth, needWidth + 2 * TUtils.node.margin)
    let needHeight = Math.max(TUtils.node.minHeight, offsetY + TUtils.zone.height + 2)

    this.resizeWidth(needWidth)
    this.background.height(needHeight)
    this.expandBtn && this.expandBtn.y(needHeight / 2)
    this.childZone && this.childZone.y(needHeight / 2)
    this.linkZone && this.linkZone.y(needHeight / 2)

    // 只有展开情况下才计算子节点位置
    if (this.isExpanding) {
      let realCount = 0
      for (let child of this.children) {
        if (child.dragging() || !child.isVisible()) continue
        realCount += 1
      }
  
      // 绘制占位
      let markerIndex = -1
      if (this.dropingChild) {
        markerIndex = this.dropingChild.knode().getAttr('@dragIndex')
        if (markerIndex >= realCount) {
          markerIndex = -1
        }
      }
  
      if (realCount == 0) {
        this.childZoneMarker.y(-4)
        this.childZoneLink.points([0, 0, 25, 0, TUtils.node.childSpace.horizonal, 0])
      }
  
      // 调整子节点位置
      let anchorY = 0
      offsetY = -this.childHeight() / 2
      for (let i = 0, j = 0; i < this.children.length; i++) {
        let child = this.children[i]
        let link = this.links[i]
  
        if (child.dragging() || !child.isVisible()) {
          link.visible(false)
          continue
        } else  if (this.isExpanding) {
          link.visible(true)
        }
  
        let childHeight = child.clientHeight()
        let height = child.size().height
        let y = offsetY + (childHeight - height) / 2
        child.knode().x(0)
        child.knode().y(y)
        
        if (this.dropingChild) {
          if (markerIndex == j) {
            this.childZoneMarker.y( offsetY - TUtils.node.childSpace.vertical / 2 - 4)
            anchorY = offsetY - TUtils.node.childSpace.vertical / 2
            this.childZoneLink.points([0, 0, 25, anchorY * 3 / 4, TUtils.node.childSpace.horizonal, anchorY])
          } else if (markerIndex == -1 && j == realCount - 1 ) {
            this.childZoneMarker.y( offsetY + childHeight + TUtils.node.childSpace.vertical / 2)
            anchorY = offsetY + childHeight + TUtils.node.childSpace.vertical / 2 + 4
            this.childZoneLink.points([0, 0, 25, anchorY * 3 / 4, TUtils.node.childSpace.horizonal, anchorY])
          }
        }
        // 调整连线
        anchorY = y + height / 2
        link.points([0, 0, 25, anchorY * 3 / 4, TUtils.node.childSpace.horizonal, anchorY])
        offsetY += (childHeight + TUtils.node.childSpace.vertical)
        j += 1
      }
    } else {
        // 绘制占位
        this.childZoneMarker.y(-4)
        this.childZoneLink.points([0, 0, 25, 0, TUtils.node.childSpace.horizonal, 0])
    }

    if (this.dropingChild) {
      this.childZoneLink.visible(true)
      this.childZoneMarker.visible(true)
    } else {
      this.childZoneLink.visible(false)
      this.childZoneMarker.visible(false)
    }
  
    if (opt.upward) {
      let p = this.parent()
      while (p) {
        p.adjust()
        p = p.parent()
      }
    }
  }

  /**
   * 
   */
  destroy (self = true, notify = true) {
     // 通知父节点删除
     if (notify) {
      let parent = this.parent()
      if (parent) {
        parent.removeChild(this)
      }
    }

    // 删除elements
    this.clearChildren()
    if (self) {
      this.root.destroy()
    }

    if (notify) {
      let stage = this.stage()
      if (stage) {
        stage.snapshot()
      }
    }
  }

  /**
   * 删除子节点
   */
  clearChildren () {
    // 删除elements
    if (this.children) {
      for (let child of this.children) {
        child.destroy(true, false)
      }
      this.children = []
    }

    if (this.links) {
      for (let link of this.links) {
        link.destroy()
      }
      this.links = []
    }

    this.adjust()
  }

  /**
   * 
   */
  toJson () {
    let json = {
      ...this.config,
      children: []
    }

    // Children
    for (let child of this.children) {
      json.children.push(child.toJson())
    }
    return json
  }

 getTitle() {
    return this.config.title
  }

  canAcceptChild () {
    return this.config.acceptChild
  }
}

export default TEntityNode
